#include "Operation.h"

using namespace std;
using namespace CICore;
using namespace CIGeom;
using namespace CIView;

/// <summary> 
/// Constructor 
/// </summary>
/// <param name="v"> A reference to the vector that contains the four 3D views pointers </param>
CIOps::Operation::Operation (vector<View*>& v)
: status (INITIAL), views (v), scene (v[0]->GetScene()) {
     
    oldSelectionMode = scene.GetSelectionMode();
    newSelectionMode = oldSelectionMode;
}

/// <summary> 
/// Destructor 
/// </summary>
CIOps::Operation::~Operation() {
    //Deleting all parameters
    vector<OperationParam*>::iterator it = parameters.begin();

    while (it != parameters.end())
        delete (*it++);
}






///<************************************************************************************************************>
///<*********************************** region Utility Private Methods *****************************************>
///<************************************************************************************************************>

/// <summary> 
/// Method called to save the vertices selection status 
/// </summary>
/// <param name="status"> A vector that will contain, for each scene's geometry, its vertices selection status </param>
/// <returns> A boolean to indicate if there is at least one vertex selected </returns>
bool CIOps::Operation::SaveVerticesSelectionStatus (std::vector< std::vector<bool> >& status) {
    map<const char*, Geometry*>& geometries = scene.GetGeometries();    //Gets a reference to the scene's geometries container
    map<const char*, Geometry*>::iterator it = geometries.begin();
    bool hasVerticesSelected = false;

    while (it != geometries.end()) {                        //For each geometry
        //Saving vertices selection status
        VertexLib& vertices = it->second->GetVertices();
        VertexIter v = vertices.begin();
        vector<bool> verticesStatus;
        
        while (v != vertices.end()) {                        //For each vertex
            verticesStatus.push_back ((*v)->selected);       //saves the vertex seletion status

            if ((*v)->selected)
                hasVerticesSelected = true;

            v++;
        }   
        status.push_back (verticesStatus);
        it++;
    }

    return hasVerticesSelected;
}


/// <summary> 
/// Method called to set the vertices selection status
/// </summary>
/// <param name="status"> A vector that contains, for each scene's geometry, its vertices selection status </param>
void CIOps::Operation::SetVerticesSelectionStatus (std::vector< std::vector<bool> >& status) {
    map<const char*, Geometry*>& geometries = scene.GetGeometries();    //Gets a reference to the scene's geometries container
    map<const char*, Geometry*>::iterator it = geometries.begin();
    unsigned int i = 0;                                                 //Represents the geometries counter
    bool hasVerticesSelected = false;

    while (it != geometries.end()) {                        //For each geometry
        //Setting vertices selection status
        VertexLib vertices = it->second->GetVertices();
        VertexIter v = vertices.begin();
        vector<bool>& verticesStatus = status[i];
        unsigned int j = 0;                                 //Represents the current geometry's vertices counter
        
        it->second->hasPrimitivesSelected = false;

        while (v != vertices.end()) {                       //For each vertex
            (*v)->selected = verticesStatus[j];             //sets the vertex selection status according to the values received

            if ((*v)->selected) {
                hasVerticesSelected = true;
                it->second->hasPrimitivesSelected = true;
            }

            j++;
            v++;
        }           

        i++;
        it++;
    }
    views[0]->SetPrimitivesSelected (hasVerticesSelected);  //Signals if some primitive has been selected
}
/// <summary> 
/// Method called to set the faces selection status
/// </summary>
/// <param name="status"> A vector that contains, for each scene's geometry, its faces selection status </param>
void CIOps::Operation::SetFacesSelectionStatus (std::vector< std::vector<bool> >& status) {
    map<const char*, Geometry*>& geometries = scene.GetGeometries();    //Gets a reference to the scene's geometries container
    map<const char*, Geometry*>::iterator it = geometries.begin();
    unsigned int i = 0;                                                 //Represents the geometries counter
    bool hasVerticesSelected = false;

    while (it != geometries.end()) {                        //For each geometry
        //Setting vertices selection status
        VertexLib vertices = it->second->GetVertices();
        VertexIter v = vertices.begin();
        vector<bool>& verticesStatus = status[i];
        unsigned int j = 0;                                 //Represents the current geometry's vertices counter
        
        it->second->hasPrimitivesSelected = false;

        while (v != vertices.end()) {                       //For each vertex
            (*v)->selected = verticesStatus[j];             //sets the vertex selection status according to the values received
            
            if ((*v)->selected) {
                hasVerticesSelected = true;
                it->second->hasPrimitivesSelected = true;
            }

            j++;
            v++;
        }         

        //Setting faces selection status
        FaceLib faces = it->second->GetFaces();
        FaceIter f = faces.begin();
        j = 0;                                              //Represents the current geometry's faces counter

        while (f != faces.end()) {                          //For each face
            bool selected = true;
    
            //If there is a face's vertex not selected, the selected boolean must be set to false
            for (unsigned short k = 0; k < (*f)->vertices.size(); k++)
                if (!(*f)->vertices[k]->selected)
                    selected = false;

            (*f)->selected = selected;                      //sets the face selection status according to the values received
            j++;
            f++;
        }          
        i++;
        it++;
    }
    views[0]->SetPrimitivesSelected (hasVerticesSelected);  //Signals if some primitive has been selected
}


/// <summary> 
/// Method called to set the visibility status
/// </summary>
/// <param name="status"> A vector that contains, for each scene's geometry, its selection status </param>
void CIOps::Operation::SetVisibilityStatus (std::vector< std::vector<bool> >& status) {
    map<const char*, Geometry*>& geometries = scene.GetGeometries();    //Gets a reference to the scene's geometries container
    map<const char*, Geometry*>::iterator it = geometries.begin();
    unsigned int i = 0;                                                 //Represents the geometries counter
    bool hasVerticesSelected = false;

    while (it != geometries.end()) {                        //For each geometry
        //Setting vertices selection status
        VertexLib vertices = it->second->GetVertices();
        VertexIter v = vertices.begin();
        vector<bool>& verticesStatus = status[i];
        unsigned int j = 0;                                 //Represents the current geometry's vertices counter
        
        it->second->hasPrimitivesSelected = false;

        while (v != vertices.end()) {                       //For each vertex
            (*v)->selected = verticesStatus[j];             //sets the vertex selection status according to the values received
            
            if ((*v)->selected) {
                (*v)->visible = true;                       //restores its visibility
                hasVerticesSelected = true;
                it->second->hasPrimitivesSelected = true;
            }

            j++;
            v++;
        }         

        //Setting faces selection status
        FaceLib faces = it->second->GetFaces();
        FaceIter f = faces.begin();
        j = 0;                                              //Represents the current geometry's faces counter

        while (f != faces.end()) {                          //For each face
            bool selected = true;
            bool visible = true;
    
            //If there is a face's vertex not selected, the selected boolean must be set to false
            //(same reasoning for the visibility)
            for (unsigned short k = 0; k < (*f)->vertices.size(); k++) {
                if (!(*f)->vertices[k]->selected) 
                    selected = false;                    

                if (!(*f)->vertices[k]->visible)
                    visible = false;
            }
                

            (*f)->selected = selected;                      //sets the face selection status according to the values received
            (*f)->visible = visible;                        //sets the face visibility status according to the values received
            j++;
            f++;
        }          
        i++;
        it++;
    }
    views[0]->SetPrimitivesSelected (hasVerticesSelected);  //Signals if some primitive has been selected
}
/***************************************************************************************************************
************************************** endregion Utility Private Methods ***************************************
****************************************************************************************************************/





///<************************************************************************************************************>
///<********************************** region Protected Utility Methods ****************************************>
///<************************************************************************************************************>

/// <summary> 
/// Method called to save old selection status
/// </summary>
/// <returns> A boolean to indicate if there is at least one vertex selected </returns>
bool CIOps::Operation::SaveOldSelection() {
    return SaveVerticesSelectionStatus (oldSelection);
}

/// <summary> 
/// Method called to save the new selection status
/// </summary>
bool CIOps::Operation::SaveNewSelection() {
    return SaveVerticesSelectionStatus (newSelection);
}
     
/// <summary> 
/// Method called to restore the old selection status 
/// </summary>
void CIOps::Operation::RestoreOldSelection() {
    scene.SetSelectionMode (oldSelectionMode);
    
    switch (oldSelectionMode) {
        case Geometry::VERTICES_SELECTION:
            SetVerticesSelectionStatus (oldSelection);
            break;

        case Geometry::FACES_SELECTION:
            SetFacesSelectionStatus (oldSelection);
            break;
    }

    //Re-computing the feedback position (if necessary)
    RestoreFeedbackPosition();
}

/// <summary> 
/// Method called to restore the new selection 
/// </summary>
void CIOps::Operation::RestoreNewSelection() {
    scene.SetSelectionMode (newSelectionMode);
    
    switch (newSelectionMode) {
        case Geometry::VERTICES_SELECTION:
            SetVerticesSelectionStatus (newSelection);
            break;

        case Geometry::FACES_SELECTION:
            SetFacesSelectionStatus (newSelection);
            break;
    }

    //Re-computing the feedback position (if necessary)
    RestoreFeedbackPosition();
}

     
/// <summary> 
/// Method called to restore the old visibility status 
/// </summary>
void CIOps::Operation::RestoreOldVisibility() {
    scene.SetSelectionMode (oldSelectionMode);

    SetVisibilityStatus (oldSelection);

    //Re-computing the feedback position (if necessary)
    RestoreFeedbackPosition();
}

/// <summary> 
/// Method called to restore the feedback position
/// </summary>
void CIOps::Operation::RestoreFeedbackPosition() {
    switch (views[0]->feedbackMode) {
        case View::FEEDBACK_TRANSLATION:            //TRANSLATION
            View::Feedback_Translation.ComputeFeedbackPosition (scene);            
            break;

        case View::FEEDBACK_ROTATION:               //ROTATION
            View::Feedback_Rotation.ComputeFeedbackPosition (scene); 
            break;

        case View::FEEDBACK_SCALING:                //SCALING
            View::Feedback_Scaling.ComputeFeedbackPosition (scene);            
            break;
    }

    for (unsigned short i = 0; i < views.size(); i++) {        
        views[i]->updateGL();
    }
}

/// <summary>
/// Method called to updates the 3D views
/// </summary>
void CIOps::Operation::UpdateViews() {
    for (unsigned short i = 0; i < views.size(); i++)
        views[i]->updateGL();
}
/***************************************************************************************************************
************************************* endregion Protected Utility Methods **************************************
****************************************************************************************************************/







///<************************************************************************************************************>
///<************************************ region Public Utility Methods *****************************************>
///<************************************************************************************************************>

/// <summary>
/// Method called to add parameterd for this operation
/// </summary>
/// <param name="param"> A pointer to the operetion parameter </param>
void CIOps::Operation::AddParameter (CIOps::OperationParam *param) {
    parameters.push_back (param);
}

/// <summary>
/// Method called to add parameterd for this operation
/// </summary>
/// <param name="name"> The parameter name </param>
/// <param name="type"> The parameter type </param>
/// <param name="value"> The parameter value </param>
/// <param name="example"> A parameter value example </param>
/// <param name="required"> A boolean to indicate if this parameter is required for this operation </param>
void CIOps::Operation::AddParameter (string& name,
                                  CIOps::OperationParam::ParamType type,
                                  string& value, 
                                  string& example, 
                                  bool required) {

    //Creating the operation parameter
    OperationParam* p = new OperationParam();
    p->name = name;
    p->type = type;
    p->value = value;
    p->example = example;
    p->required = required;

    parameters.push_back (p);
}
/***************************************************************************************************************
************************************** endregion Public Utility Methods ****************************************
****************************************************************************************************************/
